package br.ufrj.so;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

public class PaginatedThread extends Thread {


	private LinkedList<Page> workingSet;
	private String threadId;
	private List<Page> pages;
	private List<Integer> pageExecution;
	private final int MAX_NPAGES = 100;
	private boolean q = false;
	
	
	public PaginatedThread(String threadId) {
		this.threadId=threadId;
		Random random = new Random();
		int nPages = random.nextInt(MAX_NPAGES);
		
		workingSet = new LinkedList<Page>();
		pages = new ArrayList<Page>();
		pageExecution = new ArrayList<Integer>();
		
		// inicializa as paginas
		System.out.println("ThreadId= " + threadId + " - nPages= " + nPages);
		for (int i=0; i<nPages; i++) {
			pages.add(new Page(threadId, i));
		}
		
		// randomiza quantas páginas e quais páginas serão executadas
		int nPagExex = random.nextInt(MAX_NPAGES);
		// so para evitar que o numero de paginas seja igual a zero
		while (nPagExex==0) nPagExex = random.nextInt(MAX_NPAGES); 
		
		// define randomicamente a ordem de execução das páginas
		
		for (int i=0; i<nPages; i++) {
			pageExecution.add(random.nextInt(nPages));
		}
	}
	
	
	public PaginatedThread(String threadId, int nPages) {
		this.threadId=threadId;
		Random random = new Random();
		
		q = true;
		
		workingSet = new LinkedList<Page>();
		pages = new ArrayList<Page>();
		pageExecution = new ArrayList<Integer>();
		
		// inicializa as paginas
		for (int i=0; i<nPages; i++) {
			pages.add(new Page(threadId, i));
		}
				
		// define randomicamente a ordem de execução das paginas		
		for (int i=0; i<nPages; i++) {
			pageExecution.add(random.nextInt(nPages));
		}
	}
	
	// define randomicamente a ordem de execução das páginas
	@Override
	public void run() {
	
		for (int i=0; i< pageExecution.size(); i++) {			
			// "executara" as paginas na ordem gerada no construtor
			// adiciona no working set
			Page executePage = null;
			for(Page page:pages) {
				if(page.getPageNumber().equals(pageExecution.get(i))) {
					executePage = page;
					break;
				}
			}
			
			// adiciona a pagina ao working set e "executa"
			addPageToWorkingSet(executePage);		
			
			// espera dois segundos ate a execução da proxima pagina 
			try {
				sleep(2000);
			} catch (InterruptedException e) {
				new RuntimeException("Error while executing method sleep at thread " + threadId);
			}
			
			// imprime o working set
			System.out.println("Thread Id=" + threadId);
			System.out.println("Next Page to be executed= "+ executePage.getPageNumber());
			System.out.println("WorkingSet= " + printListWithPage(workingSet));
			System.out.println("Memory status= " + printListWithPage(Memory.getMemoryListInstance()));
			System.out.println("-----------------------------------");
					
			
		} // fim do for		
	}
	
	private void addPageToWorkingSet(Page page) {
		if (workingSet.contains(page)) {
			// remove para adicionar no topo (utilizada mais recentemente
			workingSet.remove(page);			
		} else if (workingSet.size() == 3) {
			// remove a ultima pagina do working set (menos utilizada)
			Page lastPage = workingSet.removeLast();
			// remove da memoria
			Memory.getMemoryListInstance().remove(lastPage);			
		}	
		// adiciona no topo da lista	
		workingSet.addFirst(page);
		if (!q) Memory.addPageToMemory(page);
		else Memory.addPageToMemoryRecent(page);
	}
	
	private void removeFromWorkingSet(Page page) {
		if (workingSet.contains(page)) {
			workingSet.remove(page);
			Memory.removePage(page);
		}
	}
	
	public String printListWithPage(List list) {
		String s = "";
		
		for (int i=0; i<list.size(); i++) {			
			s += ((Page)list.get(i)).getPageNumber() + " ";
		}
		
		return s;
	}

	
	//getters and setters
	public LinkedList<Page> getWorkingSet() {
		return workingSet;
	}

	public void setWorkingSet(LinkedList<Page> workingSet) {
		this.workingSet = workingSet;
	}

	public String getThreadId() {
		return threadId;
	}

	public void setThreadId(String threadId) {
		this.threadId = threadId;
	}

	public List<Page> getPages() {
		return pages;
	}

	public void setPages(List<Page> pages) {
		this.pages = pages;
	}
	
	

}
